module Calc;

/************************
 * Autor: Lukasz Kufel  *
 * Przedmiot: Jezyk D   *
 *    zadanie nr 2      *
 *    "Kalkulator"      *
 ************************/

import tango.io.Console;
import tango.io.Stdout;
import tango.io.FileConduit;
import tango.util.Convert;
import tango.text.stream.LineIterator;
import tango.math.Math;
import tango.text.Regex;

void main(char[][] args){
	
	auto inputStream = args.length < 2 ? Cin.stream : new FileConduit(args[1]);
	
	foreach(line; new LineIterator!(char)(inputStream)){
		
		// opuszczamy puste linie 
		if ("" == line) continue;
		
		// opuszczamy komentarze
		if (Regex("^(#|//)").test(line)) continue;
		
		debug Cout ("line: " ~ line).newline;
		auto lineInRPN = convertToRPN(line);
		debug Cout ("lineInRPN: " ~ lineInRPN).newline;
		auto result = calculate(lineInRPN);
		debug Cout ("Wynik: ");
		
		Stdout.formatln("{:g10}", result);
	}
}

char[] convertToRPN(char[] line){
	char[] rLine;
	auto stos = new Stos!(int)();
	
	foreach (c; line){
		if ((c >= '0' && c <='9') || c == '.') {
			rLine ~= c;
		} else if (c == '(') {
			stos.push(c);
		} else if (c == ')') {
			char z;
			while(!stos.pusty && ((z = stos.pop()) != '(')) {
				rLine ~= ' ';
				rLine ~= z;
				rLine ~= ' ';
			}
		} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '^'){
			rLine ~= ' ';
			char z;
			
			if (stos.pusty) {
				debug Cout ("pusty").newline;
				stos.push(c);  // stos jest pusty dopisujemy na stos nasz operator
				continue;
			}
			z = stos.head();
			
			if (z == '(') { // z moze byc tez nawiasem otwierajacym
				stos.push(c);
			} else if ((c == '^' && z != '^') 
					|| ((c == '*' || c == '/') && (z == '+' || z == '-'))) {
				stos.push(c);
			} else {
				do{
					debug Cout ("zdejmuje ze stosu").newline;
					z = stos.pop();
					rLine ~= cast(char) z;
					rLine ~= ' ';
					if (stos.pusty) break;
				}while ((stos.head() == '^')
				|| ((c == '*' || c == '/') && (stos.head() == '*' || stos.head() == '/'))
				|| ((c == '+' || c == '-') && (stos.head() == '+' || stos.head() == '-' || stos.head() == '*' || stos.head() == '/')));
				stos.push(c);
			}
			rLine ~= ' ';
		} else if (c == ' '){
			// nic nie robimy ze spacjami
		} else {
			Cout ("Nieoczekiwany znak brak obliczen").newline;
			return "";
		}
		
		//wypisanie stosu po kazdej kolejce
		debug Cout ("stos:");
		foreach(el; stos.tab){
			debug Cout (" " ~ cast(char)(el));
		}
		debug Cout ("").newline;
	}
	rLine ~= ' ';
	while(!stos.pusty) {
		rLine ~= stos.pop();
		rLine ~= ' ';
	}
	return rLine;
}

float calculate(char[] rpn){
	float wynik = 0;
	auto stos = new Stos!(float);
	int i;
	while (i < rpn.length){
		if ((rpn[i] >= '0' && rpn[i] <='9') || rpn[i] == '.') {
			debug Cout ("liczba:").newline;
			char[] liczba = "";
			while ((rpn[i] >= '0' && rpn[i] <='9') || rpn[i] == '.') {
				debug Cout (""~ rpn[i]).newline;
				liczba ~= rpn[i];
				i++;
			}
			stos.push(to!(float)(liczba));
		} else if (rpn[i] == '+') {
			auto plus = stos.pop() + stos.pop();
			debug Cout ("plus: " ~ to!(char[])(plus)).newline;
			stos.push(plus);
		} else if (rpn[i] == '-') {
			auto minus = - stos.pop() + stos.pop();
			debug Cout ("minus: " ~ to!(char[])(minus)).newline;
			stos.push(minus);
		} else if (rpn[i] == '*') {
			auto razy = stos.pop() * stos.pop();
			debug Cout ("razy: " ~ to!(char[])(razy)).newline;
			stos.push(razy);
		} else if (rpn[i] == '/') {
			auto dzielnik = stos.pop();
			auto dzielna = stos.pop();
			if (dzielnik) {
				auto przez = dzielna / dzielnik;
				debug Cout ("przez: " ~ to!(char[])(przez)).newline;
				stos.push(przez);
			} else {
				Cout ("Dzielenie przez zero ");
				return 0.0;
			}
		} else if (rpn[i] == '^') {
			auto wykladnik = stos.pop();
			auto podstawa = stos.pop();
			stos.push(pow(podstawa, wykladnik));
		}
		i++;
	}
	if (!stos.pusty) {
		wynik = stos.pop();
	}
	return wynik;
}


class Stos(T){
	T[] tab;
	
	void push(T obj) {
		tab ~= obj;
	}
	
	bool pusty() {
		return (tab.length == 0);
	}
	
	T pop() {
		//Cout ("pop " ~ to!(char[])(tab.length)).newline;
		T toReturn = tab[tab.length - 1];
		tab.length = tab.length - 1;
		return toReturn;
	}
	
	T head() {
		return tab[tab.length - 1];
	}
}